#!/bin/bash
export LANG="zh_CN.UTF-8"
echo '---------------------bash变量-------------';
:<<EOF
shell变量
    定义变量:定义变量不需要用'$'符号
    使用变量:只需要在变量明前加上美元符'$'即可
变量类型
    局部变量:仅在当前shell实例中有效,其他shell启动程序不能访问该局部变量
    环境变量(全局变量):所有程序,包括shell启动程序
    shell变量:shell变量是由shell程序设置的特殊变量.包含环境变量和局部变量
shell中特殊且重要变量
    $?  获取执行上一个指令的执行状态返回值（0为成功，非零为失败），最常用
    $_  获取在此之前执行的命令或脚本的最后一个参数，不常用
    $$  获取当前执行的shell脚本的进程号（pid），不常用
    $0  获取当前执行的shell脚本的文件名，如果脚本中包含了路径，那么就包含脚本路径
    $n  获取当前执行的shell脚本的第n个参数值，n=1..9,当n=0时获取当前脚本的文件名；如果n大于9，则用大括号括起来，例如${10},接的参数以空格隔起来
    $*  获取当前shell脚本所有传参的参数，不加引号和$@相同；给$*加上双引号,例如："$*",则表示将所有的参数视为单个字符串"$1 $2 $3 ..."
    $@  获取当前shell脚本所有传参的参数，不加引号和$*相同；给$@加上双引号,例如："$@",则表示将所有的参数视为单个字符串"$1" "$2" "$3" "..."
    $#  当前shell脚本后面接的参数的总个数
    set 设置位置参数（同命令行脚本的传参）
        [root@skinrun ~]# set -- "I am" handler phpstudy
        # 通过set设置三个字符串参数
        # -- 表示清除所有的参数变量，重新设置后面的参数变量
    eval eval就是把echo的字符串：eval "echo \$$#"
        runlevel=$(set -- $(runlevel); eval "echo \$$#" ) #当前系统运行级别
    
EOF

name='phpstudy'
#删除变量
#unset name;
#输出变量
echo "输出变量（销毁前）：${name}"
unset name;
echo "输出变量（销毁后）：${name:='变量值已被销毁，这是默认'}"

echo '------------shell内置变量命令--------------'
:<<EOF
echo
    参数选项
    -n  不换行输出内容
    -e  解析转义字符
            \n   换行
            \r   回车
            \t   制表符
            \b   退格
            \v   纵向制表符
evel 
    eval args
    当shell程序执行到eval语句时，shell读入参数args，并将他们组成一个新的命令，然后执行
    例如：runlevel=$(set -- $(runlevel); eval "echo \$$#" ) #当前系统运行级别

exec
    exec 命令参数
    exec命令能够在不创建新的子进程的前提下，转去执行指定的命令，当指定的命令执行完毕后，该进程（也就是最初的Shell）就终止了

    #当使用exec打开文件后，read命令每次都会将文件指针移动到文件的下一行进行读取，直到文件末尾，利用这个可以实现处理文件内容。
        exec < /tmp/tmp.log
        while read line
        do
            echo $line
        done
        echo ok
read
    从标准输入读取字符串等信息，传给Shell程序内部定义的变量
    read [参数] [变量名]
        -p prompt:设置提示信息
        -t timeout:设置输入等待时间，单位默认为秒

shift
    shift命令的主要作用是将位置参数$1、$2等进行左移，即如果位置参数是$3、$2、$1，那么执行一次shift后，$3就变成了$2, $2变成了$1, $1就消失了

exit
    功能：退出Shell程序。在exit之后可以有选择地指定一个数位作为返回状态.
EOF




echo '---------------------bash字符串----------';
:<<EOF
单引号、双引号、或者不要引号
    单引号里的字符串都会原样输出，单引号字符串中的变量是无效的
    单引号字符串中不能出现单独一个单引号（对单引号使用转义也不行），单成对出现，作为字符串拼接使用
双引号的优点：
    双引号里可以有变量
    双引号里可以出现转义字符
shell传递参数
    注意，$10 不能获取第十个参数，获取第十个参数需要${10}。当n>=10时，需要使用${n}来获取参数。

字符串操作
    ${parameter}    返回变量$parameter的内容
    ${#parameter}(计算速度最快) 或 echo ${parameter} | wc -L 或 expr length 或 echo ${parameter} | awk '{print length($0)}'  返回变量$parameter内容的长度（按字符），也适用于特殊变量
    ${parameter:offset} 在变量${parameter}中，从位置offset之后开始提取子串到结尾
    ${parameter:offset:length}  在变量${parameter}中，从位置offset之后开始提取长度为length的子串
    ${parameter#word}   从变量${parameter}开头开始删除最短匹配的word子串
    ${parameter##word}  从变量${parameter}开头开始删除最长匹配的word子串
    ${parameter%word}   从变量${parameter}结尾开始删除最短匹配的word子串
    ${parameter%%word}  从变量${parameter}结尾开始删除最长匹配的word子串
    ${parameter/pattern/string}  使用string替换第一个匹配的pattern
    ${parameter//pattern/string}  使用string替换所有匹配的pattern
特殊扩展变量说明
    ${parameter:-word} 
        如果parameter变量值为空或未赋值，则返回word字符串代替变量的值
        用途：如果变量未定义，则返回备用的值，防止变量为空值或因未定义导致异常
    ${parameter:=word}
        如果parameter变量值为空或未赋值，则设置这个变量值为word，并返回其值。（位置变量和特殊变量为空）
        用途：基本同上一个${parameter:-word},但是变量又额外给parameter变量赋值
    ${parameter:?word}
        如果parameter变量值为空或未赋值，那么word字符串将被作为标准错误输出，否则输出变量的值
        用途：用于捕捉由于变量未定义而导致的错误，并退出程序
    ${parameter:+word}
        如果parameter变量值为空或未赋值，则什么都不做，否则word字符串将代替变量的值        
EOF

# 1、从变量substr=abc123ABCabc
# 请编写Shell脚本以打印下面语句中字符数小于6的单词
# I am oldboy linux, welcome to our training
substr=abc123ABCabc
echo "原字符串==>${substr}"
echo ${substr#abc}
echo ${substr##abc}
echo ${substr%abc}
echo ${substr%%abc}

string="123456789"

#计算字符串的长度
echo "字符串${string}的长度为：${#string}"
echo "字符串${string}的长度为：`echo ${string} | awk '{print length($0)}'`"

echo ${string} | awk -F "" '{print NF}'
# exit 0

echo "字符串${string}的长度为：" `expr length ${string}`
# evel "expr length ${string}"
# exit 0
#截取字符串
echo "截取的字符串为：${string:1:4}"

#查找字符串6的位置，脚本命令用户反引号``,而不是单引号
echo `expr index "$string" 69`

echo "shell传递参数示例"
echo "执行的文件名：$0"
echo "第一个参数：$1"
echo "第二个参数：$2"
echo "第三个参数：$3"

echo '---------------------bash算数运算---------------'
:<<EOF
算数运算符
关系运算符
布尔运算符
字符串运算符
文件测试运算符
shell 本身不支持简单的数学运算，可以借助命令awk或expr，expr最常用
    expr是一款表达式计算工具

常见的算数运算符号
    +、-           加法（或正号）、减法（或负号）      「*常用」
    *、/、%         乘法、除法、取余（取根）           「*常用」
    **             幂运算符                         「*常用」
    ++、--         自增、自减                       「*常用」
    !、&&、||       逻辑非（取反）、逻辑与（and）、逻辑或
    <、<=、> 、>=    比较符号
    == 、!=、=      相等、不相等、赋值                  「*常用」
    <<、>>          向左移位、向右移位  
    ~、|、&、^       按位取反、按位异或、按位或
    = +=  -=  *=  /=  %=  赋值运算                     「*常用」

运算命令
    (())            用于整数运算的常用运算符，效率高
                    注意
                        "(())"表达式在命令行执行时，不需要加$符号，直接((6%3)),但是如果需要输出，就要加$符
                        "(())"里所有字符之间没有空格，有一个或多个空格都不影响结果
    let             用于整数运算，类似于"(())"

    expr            用于整数运算，还有其他功能
                    注意
                        运算符及用于计算的数字左右都至少有一个空格，否则报错
                        使用乘号时，必须用反斜线屏蔽其特定含义，因为shell可以会误解星号的含义
                        计算字符串长度  
                            expr length "$parameter"
                        判断文件扩展名是否符合要求

    bc              Linux下的一个计算器程序（适合整数和小数运算）
    $[]             用于整数运算
    awk             整数或小数运算
    declare         定义变量值和属性，-i可以用于定义整型变量、做运算
EOF

#1、(())数值运算
#用法：
a=$1
b=$2
echo "a-b=$((a-b))"
echo "a+b=$((a+b))"
echo "a*b=$((a*b))"
echo "a**b=$((a**b))"
echo "a/b=$((a/b))"
echo "a%b=$((a%b))"


echo $3=$(($3))

#2、let赋值运算
function let_fun()
{
    timeout=5
    fails=0
    success=0
    while true
    do
        wget --timeout=${timeout} --tries=1 http://www.060824.top -q -O /dev/null
        if [ $? -ne 0 ]
        then
            let fails=fails+1
        else
            let success+=1
        fi

        if [ ${success} -ge 1 ]
        then
            echo "wget files success"
            exit 0
        fi

        if [ ${fails} -ge 2 ]
        then
            echo "fails"
            exit 2;
        fi
    done
}
# let_fun



#3、expr 
echo "=========>expr运算"
expr 2 + 1
echo `expr 2 \* 3`



#判断变量或字符串是否为整数
#方法：利用expr 做计算时变量或字符串必须是整数的规则，把一个变量或字符串和一个已知的整数（非0）相加，看命令返回的值是否为0
#     如果为0，就认为该变量或字符串为整数，否则就不是整数
#   结论：利用expr做计算，将一个未知的变量和一个已知的整数相加，看返回的是否为0，如果为0就认为做加法的变量为整数，否则就不是整数
intnum=0.9
expr ${intnum} + 8 &>/dev/null
if [ $? -eq 0 ]
then
    echo "整数"
else
    echo "非法参数"
fi

#判断文件扩展名是否合法：
if expr "$1" : ".*\png" &>/dev/null
then
    echo "you are using $1"
else
    echo "please use *.pnd file"
fi



#expr计算字符串长度
str="12345678901"
echo "字符串${str}长度="`expr length ${str}`

#注意使用的是反引号 ` 而不是单引号 '
#条件表达式要放在方括号之间，并且要有空格，例如: [$a==$b] 是错误的，必须写成 [ $a == $b ]。
#乘号(*)前边必须加反斜杠(\)才能实现乘法运算；
#在 MAC 中 shell 的 expr 语法是：$((表达式))，此处表达式中的 "*" 不需要转义符号 "\" 。
function sum(){
    echo '请输入你要计算的两个数'
    read num1
    echo "num1="${num1}
    read num2
    echo "num2="${num2}
    echo ${num1}+${num2}=`expr ${num1} + ${num2} `
}

#4、bc命令 ,支持整数或小数计算
echo "=========>bc运算"
echo `seq -s '+' 10`=`seq -s ' + ' 10|bc` 
echo `seq -s '+' 10`=$((`seq -s ' + ' 10`))
echo `seq -s '+' 10`=`seq -s ' + ' 10|xargs expr`


#5、awk计算,支持整数或小数计算
echo "7.7 3.8" | awk '{print ($1-$2)}'
echo `seq -s ' ' 3` | awk '{print($1+$2+$3)}'

#6、declare(同typeset)命令的用法
echo "=========>declare命令"
declare -i A=30 B=70
echo ${A}
echo ${B}
echo $[$A+$B]
# exit 0
# $[]符号元算
echo "=========>\$[]符号运算"
i=5
i=$[i+5]
echo ${i}

a=2
b=3
echo "${a}+${b}="$[2+3]
echo "${a}-${b}="$[2-3]
echo "${a}*${b}="$[2*3]
echo "${a}/${b}="$[2/3]
echo "${a}%${b}="$[2%3]
echo "${a}**${b}="$[2**3]

#打印杨辉三角
function triangle()
{
    if ( test -z $1 )
    then
        read -p "Input Max Lines:" MAX
    else
        MAX=$1
    fi
    
    #打印三角
    i=1
    while [ $i -le $MAX ]
    do
        j=1
        while [ $j -le $i ]
        do
            f=$[i-1]
            g=$[j-1]
            if [ $j -eq $i ] || [ $j -eq 1 ]
            then
                declare SUM_${i}_$j=1
            else
                declare A=$[SUM_${f}_$j]
                declare B=$[SUM_${f}_$g]
                declare SUM_${i}_$j=`expr $A + $B`
            fi
            echo -en $[SUM_${i}_$j]
            let j++
        done
        let i++
    done
}
# triangle


echo '---------------------bash条件判断---------------'
:<<EOF
条件测试
    test <测试表达式>
        test与表达式之间至少有一个空格
            -f 测试文件是否为普通文件
            -z 测试字符串长度为0，则表达式成立
    [ 测试表达式 ]
        []的边界和内容之间至少有一个空格
    [[ 测试表达式 ]]
        常用于计算
        可以使用通配符，进行模式匹配
        [[]]的边界和内容之间至少有一个空格
        &&、||、>、<等操作，不能用于[]中
    ((测试表达式))
        一般用于if语句里。
        (())两端不需要有空格
        可用于整数运算
        -a、-o、-gt(用于整数)、-lt(用于整数)代替上述操作符

文件测试:man test 查询更多命令
    -d directory:文件存在且为目录则为真，即测试表达式成立
    -f file:文件存在且为普通文件则为真，即测试表达式成立
    -e exist:文件或目录（不区分）存在则为真
    -r read :文件存在且文件大小不能为0则为真，即表达式成立
    -w write :文件存在且文件可写则为真，即测试表达式成立
    -x executable:文件存在且可执行则为真，即测试表达式成立
    -L link：文件存在且为链接文件则为真，即测试表达式成立
    f1 -nt f2 ,nt为newer than:文件f1比文件f2新则为真，根据修改的时间来计算
    f1 -ot f2 ,ot为older than:文件f1比文件f2旧则为真，根据修改的时间来计算        

特殊条件测试表达式案例
    [ 条件1 ] && {
        命令1
        命令2
        ...
    }

    [[ 条件1 ]] && {
        命令1
        命令2
        ...
    }

    test 条件1 && {
        命令1
        命令2
        ...
    }
    
    总结：以上三种判断等价于

    if [ 条件1 ]
    then
        命令1
        命令2
        ...
    fi
字符串测试表达式
    -n "字符串"：若字符串的长度不为0，则为真，即表达式成立，n可以理解为no zero
    -z "字符串"：若字符串的长度为0，则为真，即测试表达式成立，z 理解为zero缩写
    "串1"="串2"：若符串1等于字符串2，则为真，即测试表达式成立，“==”可代替“=”
    "串1"!="串2"：若符串1不等于字符串2，则为真，即测试表达式成立，“!==”可代替“!=”
    说明：
        对于字符串测试，一定要加双引号之后再进行比较，如[ -n "$project" ]
        比较符号（= 和 !=）两端一定要有空格
        "!="和"="可用于比较两个字符串是否相同

整数二元比较操作符
    []和test中的比较符
        -eq     equal
        -ne     not equal
        -ge     grater equal
        -le     less equal
        -lt     less than
        -gt     grater than
    (())和[[]]中的比较符号
        === 或 ==
        !=
        >=
        <=
        <
        >
    整数比较大小：(())优于[[]],推荐使用[] ,(())次之，不推荐使用[[]]
逻辑操作符
    []和test中的比较符
        -a  and，两端都为真，则结果为真
        -o  or，两端有一个为真，则结果为真
        !   not，两端相反，则结果为真
    (())和[[]]中的比较符号
        &&
        ||
        !
    多个[]时，外部用&&或||连接：[ "$num" -gt 0 ] && [ "$num" -lt 10  ]

测试表达式的test []  [[]] (()) 的区别总结
    测试表达式符号            []             test                   [[]]                        (())
    边界为是否需要空格        需要             需要                   需要                         需要
    逻辑操作符              ! -a -o          ! -a -o                && || !                     && || !
    整数比较操作符           -eq -gt -le     -eq -gt -le            -eq -gt -le -ge -lt         = > <= >= <
                          -ge -lt           -ge -lt              或 =  >  <=  >=  <
    字符串比较操作符        == === !=        == === !               == === !                    == === !
    是否支持通配符          不支持             不支持                 支持                          不支持

EOF
if [ -d /home/test ]
then
    echo '是文件夹'
else
    echo '不存在'
fi

# echo -n 'please input a char ot number'
function careNumber()
{
    read -p 'please input a char ot number：' var
    [ "$var" -eq 1 ] && {
        echo 1
        exit 0
    }

    [ "$var" -eq 2 ] && {
        echo 2
        exit 0
    }

    [ "$var" -ne "1" -a "$var" -ne "2" ] && {
        echo error
        exit 0
    }
}
# careNumber
echo '---------------------if条件语句---------------'
:<<EOF
语法格式1
    if 条件表达式
        then
            指令

    fi
或
    if 条件表达式; then
        指令
    fi

条件嵌套
    if 条件表达式
    then
        指令
        if 条件表达式
        then
            指令
        fi
    fi
双分支语法
    if 条件表达式
    then
        指令集1
    else
        指令集2
    fi

多分支语法
    if 条件表达式
    then
        指令集1
    elif
        指令集2
    else
        指令集3
    fi
EOF
:<<EOF if条件表达式语法

test表达式

    if test 表达式
    then
        指令
    fi

[]表达式
    if [ 字符串或算数表达式 ] 
    then
        指令
    fi

[[]]表达式
    if [[ 字符串表达式 ]]
    then
        指令
    fi

(())条件表达式
    if ((算数表达式))
    then
        指令
    fi

命令表达式
    if 命令
    then
        指令
    fi
EOF

# 监测服务端口是否通畅
IP_ADDR=api.skinrun.cn
PORT=80
if [ `nmap ${IP_ADDR} -p ${PORT} | grep open | wc -l` ]
then
    echo ${PORT}'端口畅通'
else
    echo ${PORT}'端口未开启'
fi


#判断字符串长度是不是为0
if [ -z "phpstudy" ]
then
    echo 1
else
    echo 0
fi

# ./example/linux.sh

echo '---------------------bash函数---------------'
:<<EOF
格式
    function 函数名() {
        指令...
        return n
    }

    #简单写法

    function 函数名 {
        指令...
        return n
    }

    函数名() {
        指令...
        return n
    }
函数的执行
    1）执行不带参数的函数时，直接输入函数名即可（不带小括号）
       格式 ：函数名
    2）函数的定义必须要在执行的程序前面定义或加载
    3）shell执行系统中各种程序的执行顺序为：
        系统别名->函数->系统命令->可执行文件
    4）函数执行时，会调用它的脚本共用变量，也可以为函数设定局部变量即特殊位置参数
    5）shell函数里面，return命令的功能与exit类似，
        return的作用是退出函数，
        而exit是退出脚本
    6）retun会返回一个退出值（即返回值）给调用函数的当前程序，而exit会返回一个退出值（即返回值）给执行程序的当前shell
    7）如果将脚本放在独立的文件中，被脚本加载使用时，需要使用source或“.”来加载
    8）函数内使用local定义局部变量，这些变量离开函数就会消失

带参数的函数
    函数名 参数1 参数2
    1）shell位置参数($1,$2...,$#,$*,$?及$@)都可以作为函数的参数使用
    2）此时父脚本的参数临时地被函数参数所掩盖或隐藏
    3）$0 比较特殊，它仍然是父脚本的名称
    4）当函数执行完成时，原来的命令行脚本的参数即可恢复
    5）函数的参数变量是在函数体里面定义的。
EOF
testDemo(){
 echo "这是我的第一个函数"
 echo "第一个参数：$1"
 echo "第二个参数：$2"
 echo "所有参数 $*"
 echo "所有参数 $@"
}
# echo '调用函数执行'
testDemo $1 $2

echo "当前脚本运行进程ID：$$ $!"
echo "脚本运行的最后一个进程ID号: $!"

echo '---------------------bash case条件语句---------------'
:<<EOF
格式：
    case "变量" in
        值1) 
            指令1...
            ;;
        值2）
            指令2...
            ;;
        *)
            指令3...
            ;;
    esac
    #说明，当变量的值等于1时，执行指令1；当变量等于2时，执行指令2；如果都不符合，则执行*）
EOF

function find_boy(){
    read -p "请输入你找男朋友的条件（房子、车子、票子、潜力股）：" vars
    case ${vars} in
        "房子")
            echo -e "\E[1;31m 嫁给你 \E[0m" 
            ;;
        "车子")
            echo -e "\E[1;2m 可以考虑嫁给你 \E[0m"
            ;;
        "票子")
            echo -e "\E[1;3m赶紧跟你走😂\E[0m"
            ;;
        *)
            echo -e "\E[1;5m 潜力股 \E[0m"
            ;;
    esac
}

echo '---------------------bash while循环语句---------------'
:<<EOF
while格式：在条件表达式成立时，进入循环指令；条件表达式不成立时，终止循环。
    while 条件表达式
    do
        指令...
    done

    实例：
    while循环按行读文件的方式
        方式1)
            exec <FILE
            sum=0
            while read line
            do
                cmd
            done
        方式2）
            cat file_PATH | while read line
            do
                cmd
            done
        方式3）
            while read line
            do
                cmd
            done < FILE

until格式：在条件表达式不成立时，进入循环指令；条件表达式成立时，终止循环。
    until 条件表达式
    do
        指令...
    done
实战案例见：example/charg_phone.sh
EOF


while read line 
do
    echo $line | awk '{print $1}'
done < /etc/hosts

exec < /etc/hosts
while read line
do
    echo $line | awk '{print $2}'
done

echo '---------------------bash脚本挂起---------------'
:<<EOF
是脚本进程挂起方法
    1）在脚本的结尾出加一个&符号
    2）nohup 脚本  &
    3）screen保持当前会画状态
进程管理命令
    1）kill、killall、pkill：杀掉进程
    2）ps：查看进程
    3）pstree：显示进程状态树
    4）top：显示进程
    5）renice：改变优先权
    6）nohup：用户退出系统之后继续工作
    7）pgrep：查找匹配条件的进程
    8）strace：跟踪一个进程的系统调用情况
    9）lstrace：跟踪进程调用库函数的情况
EOF

function charger_phone(){
    remain=1000
    i=15
    while ((remain>=i))
    do
        ((remain=remain-i))
        [ $remain -lt $i ] && {
            echo "money is not enough:${remain}"
            break
        }
        sleep 1
        echo "send message,remain ${remain}"
    done
    
}
# charger_phone


#每2秒钟打印一次系统负载信息
function print_load(){
    while true
    do
        uptime 
        sleep 2
    done
}
# print_load

echo '---------------------bash for循环---------------'
:<<EOF
语法一
    for 变量 in 变量取值列表
    do
        指令...
    done

    #注意
        “in 变量取制列表”可以省略，省略时相当于in "$@",也就是使用for i就相当于for i in $@
    实例：
        for 女人 in 世界上所有女人
        do
            if [ 漂亮 ] && [ 皮肤白 ] && [ 温柔似水 ] && [ 高挑 ]
                then
                echo "男人的追求"
            else
                echo "不喜欢"
            fi
        done
语法二
    for((exp1;exp2;exp3))
    do
        指令...
    done

EOF
#输出序列5～1
for num in {5..1}
do
    echo ${num}
done

for n in `seq 5 -1 1`
do
    echo ${n}
done

#遍历指定目录下的所有文件或目录
function scan_dir(){
    for filename in `ls /home/test`
    do
        echo ${filename}
    done
}
scan_dir
# exit 0

function nine(){
    color='\E[47;30m'
    res='\E[0m'
    for i in `seq 9`
    do
        for j in `seq 9`
        do
            if [ $i -ge $j ]
                then
                # sleep 1
                echo -en "${color}${i}x${j}=$((i*j))${res}\t"
            fi
        done
        echo " "
    done    
}
nine
# exit 0

echo '---------------------bash 产生随机数6中方式---------------'
:<<EOF
    1、通过系统环境变量（$RANDOM）实现，随机数范围为0～32767
        echo $RANDOM
        改进：通过在输出的随机数后增加加密字符串的方式解决，最后在一起执行md5sum操作并截取结果的后n位
        echo "salt${RANDOM}" |md5sum | cut -c 8-15
    2、通过openssl产生随机数
        openssl rand -base64 length
    3、通过时间(date)获得随机数
        date +%s%N
    4、通过/dev/urandom配合chksum生成随机数。
        head /dev/urandom | cksum
        说明：/dev/urandom设备存储着系统当前运行环境的实时数据。
        可以通过读取文件的形式读取里边的数据。并且它是非阻塞的随机数发生器，该读取操作不会产生阻塞。
    5、通过UUID生成随机数(UUID,被称为通用唯一识别码)
        cat /proc/sys/kernel/random/uuid
EOF
echo `${RANDOM} | md5sum | cut -c 1-20`
echo `openssl rand -base64 20`
echo '---------------------select循环语句---------------'
:<<EOF
select循环语句的主要作用可能就是创建菜单，在执行带select循环语句的脚本时，输出会按照数字顺序的列表显示一个菜单项，并显示提示符（默认是#? ）
在这种select循环语句的语法中，在执行脚本后，select关键字后面会有一个“变量名”，变量名依次获取in关键字后面的变量取值列表内容（以空格分隔），
每次仅取一个，然后进入循环（do和done之间），执行循环内的所有指令，当执行到done时结束返回，之后，“变量名”再继续取变量列表里的下一个变量值，
继续执行循环内的所有指令（do和done之间的指令），当执行到done时结束返回，以此类推，直到取完最后一个变量列表里的值并进入循环执行到done结束为止。
与for循环不同的是，select循环执行后会出现菜单项等待用户选择（不会自动循环所有变量列表），而用户输入的只能是菜单项前面的数字序号，每输入一次对应的序号就会执行一次循环，直到变量后面对应列表取完为止
格式：
    select 变量名 [in 菜单取值列表]
    do
        指令...
    done
    注：在此结构中“in变量取值列表”可省略，省略时相当于使用in“$@”，使用for i就相当于使用for i in“$@”。

EOF

# select name in c++ c php golang swoole
# do
#     echo $name;
# done

echo "===>采用数组变量列表"
array=(c++ c php golang swoole)
# select name in "${array[@]}"
# do
#     echo $name
# done

PS3="please select a num from menu："

select name in "${array[@]}"
do
    echo -e "I guess you selected the menu is :\n $REPLY) $name"

done

echo '---------------------循环控制及状态值返回的应用---------------'
:<<EOF
    break（循环控制）
        break n
        如果省略n,则表示跳出整个循环，n表示跳出循环的层数
    continue（循环控制）
        continue n
        如果省略n,则表示跳过本次循环，忽略本次循环循环的剩余代码，进入循环的下一次循环。
        n表示退出到第n层继续循环
    exit（退出脚本）
        exit n
        退出当前shell程序，n为上一次程序执行的状态返回值。n可以省略，在下一个shell里可通过
        "$?"接收exit n的n值
    return（退出函数)
        用于在函数里作为函数的返回值，以判断函数执行是否正确。
        在下一个shell里可通过"$?"接收exit n的n值。
    
EOF

echo '---------------------bash数组---------------'
:<<EOF
方法1:(推荐)
    用小括号将变量值括起来赋值给数组变量，每个变量值之间要用空格进行分隔。
    array=(value1 value2 value3 ...)

方法2:
    用小括号将变量值括起来，同时采用健值对的形式赋值
    array=([1]=>one [2]=>two [3]=>three)
方法3:
    分别定义数组变量的方法来定义。
    array[0]=a; array[1]=b; array[2]=c
EOF

arr=(1 2 3 4 5 6)
#读取数组元素
echo ${arr[0]}
#打印整个数组下标用*或@匹配所有元素
echo ${arr[*]}
echo ${arr[@]}
#删除数组
unset arr[0]
echo ${arr[@]}
#arr数组分片
echo ${arr[@]:0:3}

#切片数组赋值为新数组newarr
#直接通过 ${数组名[@或*]:起始位置:长度} 切片原先数组，返回是字符串，中间用“空格”分开，因此如果加上”()”，将得到切片数组，上面例子：newarr 就是一个新数据
newarr=(${arr[*]:0:2})
echo "新数组newarr为:${newarr[*]}"
#调用方法是：${数组名[@或*]/查找字符/替换字符} 该操作不会改变原先数组内容，如果需要修改，可以看上面例子，重新定义数据。
newarr2=(${arr[*]/3/99})
echo "新数组newarr2为:${newarr2[*]}"
#获取数组的长度：获取数组长度的方法与获取字符串长度的方法相同
echo "arr数组元素:${arr[@]}"
echo "arr数组长度:${#arr[@]}"
arr1=(1 2 3 4 5 6)
echo "从左开始匹配最短的数组元素,并删除：${arr1[@]#o *}" 
echo "从左开始匹配最长的数组元素,并删除：${arr1[@]##o *}" 
arr2=(1 2 3 4 5 6)
echo "从右开始匹配最短的数组元素,并删除：${arr2[@]%f 2}" 
echo "从右开始匹配最长的数组元素,并删除：${arr2[@]%%f 2}" 
# echo ${arr[@]}


# ------------------------综合实践-------------------

test=(golang c php shell python c++ java c# lua)
for (( i=0;i<${#test[*]};i++))
do
    echo ${test[$i]}
done

echo "-----------动态遍历当前目录下的文件或文件夹-----"
for val in $(ls)
do
    echo ${val}
done


echo "-----实战案例1：利用bash for循环打印：I am oldboy teacher welcome to oldboy traiting class,中字母数不大于6的单词？"
words=(I am oldboy teacher welcome to oldboy traiting class)
for val in ${words[*]}
do
    if [ ${#val} -le 6 ]
    then
        echo ${val}
    fi
done

echo --------------------
for val in ${words[@]}
do
    if [ `expr length ${val}` -le 6 ]
        then
        echo ${val}
    fi
done

echo ------------定义字符串
strs="I am oldboy teacher welcome to oldboy traiting class"
for val in ${strs}
do
    if [ `expr length ${val}` -le 6 ]
        then
        echo ${val}
    fi
done

echo --------------变量引用规范
:<<EOF
1、在引用变量时，若变量前后都有字符，则需要使用${params}加大括号方式引用变量，以防止产生起义
2、当变量内容为字符串时，需要使用"${params}",外面加双引号的方式引用变量；
3、当变量为整数时，则最好使用$params引用
4、crontab定时任务执行shell时，使用环境变量，需要重新定义，避免产生起义
全局变量、局部变量、函数变量、数组变量都是如此。

EOF

echo --------------函数命名和定义规范
:<<EOF
1、shell函数的命名可采用单词首字母大写形式L：如CreateFile(),并且语义要清晰。

EOF

echo --------------shell脚本的代码架构
:<<EOF
1、shell通用变量以配置文件形式单独存放，如：nginx.conf
EOF

echo --------------shell脚本的代码调试
:<<EOF
set -n  读命令但并不执行
set -v  显示读取的所有行
set -x  显示所有命令及其参数

提示：set -x 命令开启调试功能，而set +x 关闭调试功能
EOF

echo -------linux信号说明

:<<EOF
HUP(1)  挂起，通常因终端掉线或用户退出而引发
INT(2)  中断，通常因按Ctrl+c组合键而引发
QUIT(3)  退出 ，通常因按下Ctrl+\组合键而引发
ABRT(6)  中止，通常由于某些严重的执行错误而引发
ALRM(14)  报警，通常用来处理超时
TERM（15） 终止，系统关机时发送
TSTP（20） 停止进程的运行，但该信号可以被处理和忽略，按Ctrl+z


EOF

echo ---------Expect实现交互式功能（自动化运维）
